#include "problem.hh"

#include <algorithm>
#include <sstream>

problem::problem(const std::string& name)
{
  lp = glp_create_prob();
  glp_set_prob_name(lp, name.c_str());
}

void problem::add(variable v)
{
  variables.push_back(v);
}

void problem::add(constraint c)
{
  constraints.push_back(c);
}

void problem::add(objective_function f)
{
  this->f = f;
}

void problem::run()
{
  const unsigned nb_matrix_cells = constraints.size() * variables.size();

  int ia[1 + nb_matrix_cells];
  int ja[1 + nb_matrix_cells];
  double ar[1 + nb_matrix_cells];

  glp_set_obj_dir(lp, f.get_dir());

  //  std::cout << "Rows definitions" << std::endl;

  // Rows definitions
  glp_add_rows(lp, constraints.size());

  for (unsigned i = 0; i < constraints.size(); ++i)
    {
      glp_set_row_name(lp, i + 1, constraints[i].get_name().c_str());

      glp_set_row_bnds(lp, i + 1,
		       constraints[i].get_bound().get_type(), 
		       constraints[i].get_bound().get_lvalue(),
		       constraints[i].get_bound().get_rvalue());
    }

  //  std::cout << "Cols definitions" << std::endl;

  // Cols definitions
  glp_add_cols(lp, variables.size());

  for (unsigned i = 0; i < variables.size(); ++i)
    {
      //      std::cout << variables[i].get_name() << std::endl;
      //      std::cout << f.get_coeff(variables[i]) << std::endl;

      glp_set_col_name(lp, i + 1, variables[i].get_name().c_str());

      glp_set_col_bnds(lp, i + 1,
		       variables[i].get_bound().get_type(),
		       variables[i].get_bound().get_lvalue(),
		       variables[i].get_bound().get_rvalue());

      
      glp_set_obj_coef(lp, i + 1, f.get_coeff(variables[i]));

      glp_set_col_kind(lp, i + 1, GLP_IV);
    }

  //  std::cout << "matrice definition" << std::endl;

  // Matrice definition
  for (unsigned i = 0; i < constraints.size(); ++i)
    {
      for (unsigned j = 0; j < variables.size(); ++j)
	{
	  ia[1 + i * variables.size() + j] = i + 1;
	  ja[1 + i * variables.size() + j] = j + 1;
	  
	  ar[1 + i * variables.size() + j] =
	    constraints[i].get_coeff(variables[j]);
	}
    }

  //  std::cout << "end matrice definition" << std::endl;



  glp_load_matrix(lp, nb_matrix_cells, ia, ja, ar);

  glp_simplex(lp, NULL);

  glp_intopt(lp,NULL);
}

problem::~problem()
{
  glp_delete_prob(lp);
}


std::ostream& operator << (std::ostream& stream, const problem& w)
{
  stream << w.f << std::endl;
  for (unsigned i = 0; i < w.constraints.size(); ++i)
    stream << w.constraints[i] << std::endl;
 

 return stream;
}

double problem::to_result(const variable& v) const
{
  std::vector<variable>::const_iterator it =
    std::find(variables.begin(), variables.end(), v);
  
  if (it == variables.end())
    throw VariableUndefined(v.get_name());
  
  return glp_mip_col_val(lp, it - variables.begin() + 1);
}

const variable& problem::var(const std::string& name) const
{
  std::vector<variable>::const_iterator it =
    std::find(variables.begin(), variables.end(), name);
  
  if (it == variables.end())
    throw VariableUndefined(name);

  return *it;
}

void problem::display_result() const
{
  std::vector<variable>::const_iterator it;

  std::cout << "result = {" << std::endl;

  std::cout << "z = " << glp_mip_obj_val(lp) << std::endl;
  for (it = variables.begin(); it != variables.end(); ++it)
    {
      std::cout << it->get_name() << " = " << to_result(*it) << ","
		<< std::endl;
    }

  std::cout << "}" << std::endl;
}

VariableUndefined::VariableUndefined(const std::string& var_name)
{
  this->var_name = var_name;
}

const char* VariableUndefined::what() const throw()
{
  std::ostringstream msg;

  msg << "Variable \"" << var_name << "\" has never been defined";

  return msg.str().c_str();

}
